/*
 *	Generate the syscall functions
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "syscall_name.h"

static char namebuf[128];

static void write_call(int n)
{
  FILE *fp;
  int saves = 0;
  snprintf(namebuf, 128, "fuzix68000/syscall_%s.S", syscall_name[n]);
  fp = fopen(namebuf, "w");
  if (fp == NULL) {
    perror(namebuf);
    exit(1);
  }
  /*
   *	We define _fork() as trashing all the registers except the return
   *	values a5 and a7. The userspace ick here allows us to avoid saving
   *	all the registers on syscall entry just in case we are doing a fork and
   *	have multiple udata blocks.
   *
   *	Note: this does mean if you want a register global you need to use a5
   *	or ensure you never take a signal in the fork() area - doable but ugly.
   */
  fprintf(fp, "\t.text\n\n"
	      "\t.globl %1$s\n\n"
	      "%1$s:\n", syscall_name[n]);
  fprintf(fp, ".mri 1\n");

  if (strcmp(syscall_name[n], "_fork") == 0) {
    /* Fork may trash everything except A5 in order to make our syscall
       path much faster */
    fprintf(fp, "\tmovem.l d2-d7/a2-a4/a6,-(sp)\n");
    fprintf(fp, "\tmove.l 44(sp),d1\n");	/* _fork argument */
    saves = 1;
  } else {
    switch(syscall_args[n]) {
      case 4:
        fprintf(fp, "\tmove.l a2,-(sp)\n");
        fprintf(fp, "\tmove.l 20(sp),a2\n");
        fprintf(fp, "\tmove.l 16(sp),a1\n");
        fprintf(fp, "\tmove.l 12(sp),a0\n");
        fprintf(fp, "\tmove.l 8(sp),d1\n");
        break;
      case VARARGS:	/* We have no 4 argument varargs.. */
      case 3:
        fprintf(fp, "\tmove.l 12(sp),a1\n");
      case 2:
        fprintf(fp, "\tmove.l 8(sp),a0\n");
      case 1:
        fprintf(fp, "\tmove.l 4(sp),d1\n");
      case 0:
        break;
    }
  }
  fprintf(fp, "\tmove.w #%d,d0\n", n);
  fprintf(fp, "\ttrap #12\n");

  if (syscall_args[n] == 4)
    fprintf(fp, "\tmove.l (sp)+,a2\n");
  else if (saves == 1)
    fprintf(fp, "\tmovem.l (sp)+,d2-d7/a2-a4/a6\n");

  /* ext is the same speed as tst so we might as well do the ext in case
     it is an error code. We have to ext the error because while we use
     shorts in kernel for such things and speed the standard says errno
     is integer */

  fprintf(fp, "\text.l d1\n"
              "\tbne _error\n");
  fprintf(fp, "\trts\n"
              "_error:\n"
              "\tmove.l d1,errno\n");
  fprintf(fp, "\trts\n");

  fclose(fp);
}

static void write_call_table(void)
{
  int i;
  for (i = 0; i < NR_SYSCALL; i++)
    write_call(i);
}

static void write_makefile(void)
{
  int i;
  FILE *fp = fopen("fuzix68000/Makefile", "w");
  if (fp == NULL) {
    perror("Makefile");
    exit(1);
  }
  fprintf(fp, "# Autogenerated by tools/syscall_68000\n");
  fprintf(fp, "CROSS_AS=m68k-uclinux-gcc\nCROSS_LD=m68k-uclinux-ld\nCROSS_AR=m68k-uclinux-ar\n");
  fprintf(fp, "ASOPTS=\n\n");
  fprintf(fp, "ASRCS = syscall_%s.S\n", syscall_name[0]);
  for (i = 1; i < NR_SYSCALL; i++)
    fprintf(fp, "ASRCS += syscall_%s.S\n", syscall_name[i]);
  fprintf(fp, "\n\nASRCALL = $(ASRCS) $(ASYS)\n");
  fprintf(fp, "\nAOBJS = $(ASRCALL:.S=.o)\n\n");
  fprintf(fp, "syslib.lib: $(AOBJS)\n");
  fprintf(fp, "\techo $(AOBJS) >syslib.l\n");
  fprintf(fp, "\t$(CROSS_AR) rc syslib.lib $(AOBJS)\n\n");
  fprintf(fp, "$(AOBJS): %%.o: %%.S\n");
  fprintf(fp, "\t$(CROSS_AS) $(ASOPTS) -c $<\n\n");
  fprintf(fp, "clean:\n");
  fprintf(fp, "\trm -f $(AOBJS) $(ASRCS) syslib.lib syslib.l *~\n\n");
  fclose(fp);
}

int main(int argc, char *argv[])
{
  write_makefile();
  write_call_table();
  exit(0);
}
